using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
using Tools;

namespace Core.Net
{
    public interface IUSocketEventListener
    {
        void USocketEvent_OnMessage(USocketGroup group, Frame frame);

        void USocketEvent_OnClose(USocketGroup group, bool serverClose);

        void USocketEvent_OnIdle(USocketGroup group);

        void USocketEvent_OnConnect(USocketGroup group, bool success);

        void USocketEvent_OnTimeOut(USocketGroup group);

        void USocketEvent_OnError(USocketGroup group, string errMsg);
    }

    public class USocketGroup
    {
        public IUSocketEventListener EventListener;

        public USocket[] Sockets;

        //线程休眠时间
        public int ThreadSleepTime = 50;

        public void AddListener(IUSocketEventListener l)
        {
            EventListener = l;
        }

        public void RemoveListener(IUSocketEventListener l)
        {
            EventListener = null;
        }

        public bool Connect(string ip, int port, bool isAsyc = true, int timeout = -1)
        {
            Sockets = new USocket[1];
            var socket = new USocket {Group = this};
            Sockets[0] = socket;
            Debug.Log($"Connecting：{ip},{port}");
            return socket.Connect(ip, port, isAsyc, timeout);
        }


        public void AllClose()
        {
            Debug.Log("Close Socket");
            for (var i = 0; i < Sockets.Length; i++)
            {
                var s = Sockets[i];
                s?.Close();
            }
        }

        public void Send(Frame f)
        {
            // if (!Settings.UseWeb)
            // {
            //     Sockets[0].Send(f);
            // }
            // else
            {
                Sockets[0].Send(f);
            }
        }

        public bool IsAllSocketConnected()
        {
            if (Sockets == null) return false;
            for (int i = 0; i < Sockets.Length; i++)
            {
                if (Sockets[i] == null || Sockets[i].status != USocket.eSTATUS.STATUS_CONNECTED) return false;
            }
            return true;
        }

        public bool IsAllSocketClosed()
        {
            if (Sockets == null) return true;
            for (var i = 0; i < Sockets.Length; i++)
            {
                if (Sockets[i] != null && Sockets[i].status != USocket.eSTATUS.STATUS_CLOSED) return false;
            }
            return true;
        }

        public void USocket_OnMessage(USocket socket, Frame f)
        {
            EventListener?.USocketEvent_OnMessage(this, f);
        }

        public void USocket_OnClose(USocket socket, bool b)
        {
            EventListener?.USocketEvent_OnClose(this, b);
        }

        public void USocket_OnIdle(USocket socket)
        {
            EventListener?.USocketEvent_OnIdle(this);
        }

        public void USocket_OnConnect(USocket socket, bool b)
        {
            //还要等其他连接，不处理消息
            for (var i = 0; i < Sockets.Length; i++)
            {
                var s = Sockets[i];
                if (s == null || s.status == USocket.eSTATUS.STATUS_INIT || s.status == USocket.eSTATUS.STATUS_CONNECTING)
                {
                    return;
                }
            }
            for (var i = 0; i < Sockets.Length; i++)
            {
                var s = Sockets[i];
                //连接算失败了
                if (s.status == USocket.eSTATUS.STATUS_CLOSED)
                {
                    EventListener?.USocketEvent_OnConnect(this, false);
                    return;
                }
            }
            //这个应该算完成了。
            EventListener?.USocketEvent_OnConnect(this, b);
        }

        public void USocket_OnTimeOut(USocket socket)
        {
            EventListener?.USocketEvent_OnTimeOut(this);
        }

        public void USocket_OnError(USocket socket, string s)
        {
#if UNITY_EDITOR
            Debug.LogError(s);
#endif
            EventListener?.USocketEvent_OnError(this, s);
        }
    }


    /// <summary>
    /// Socket同步和异步抽象的封装
    /// </summary>
    public class USocket
    {
        public USocketGroup Group;
        private System.Net.Sockets.Socket clientSocket;
        public Protocal Protocal { get; private set; }
        //服务器IP
        public string IP { get; private set; }
        //服务器端口
        public int PORT { get; private set; }
        //客户端IP
        public string LocalIP { get; private set; }
        //客户端端口
        public int LocalPORT { get; private set; }
        public eSTATUS status { get; private set; }
        private bool asycRead = false; // 配置是异步收取还是同步接收
        private bool asycSend = false; // 配置是异步收取还是同步接收
        //发送缓冲
        private ConcurrentQueue<Frame> _SendBuff = new ConcurrentQueue<Frame>();
        private bool serverClose = true; //服务器主动关闭  
        public int TimeOut = 100; //10*1000*1000;  //100;//-1;// 超时时间 微秒
        public int ConnectTimeout = 5 * 1000;

        public enum eSTATUS
        {
            STATUS_INIT = 0,
            STATUS_CONNECTING = 1,
            STATUS_CONNECTED = 2,
            STATUS_CLOSED = 3
        }

        private ByteBuf buf;

        /**
         * 构造（但不完善，需要设置监听器和协议解析器）
         */
        public USocket()
        {
            Protocal p = new LVProtocal();
            SetProtocal(p);
            buf = new ByteBuf(4096);
        }

        /**
         * 构造
         */
        public USocket(Protocal protocal)
        {
            SetProtocal(protocal);
            buf = new ByteBuf(4096);
        }

        /**
         * 设置协议
         */
        public Protocal SetProtocal(Protocal p)
        {
            return Protocal = p;
        }

        /// <summary>
        /// 连接指定地址
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="timeout">最大超时时间</param>
        /// <param name="isAsyc">是否是异步连接服务器,如果异步，连接成功与否均返回true</param>
        /// <returns></returns>
        public bool Connect(string ip, int port, bool isAsyc = true, int timeout = -1)
        {
            status = eSTATUS.STATUS_CONNECTING;
            IP = ip;
            PORT = port;
            if (!isAsyc)
            {
                var addresses = Dns.GetHostAddresses(ip);
                if (addresses.Length > 0)
                {
                    GenClientSocket(addresses[0].AddressFamily == AddressFamily.InterNetworkV6);
                    clientSocket.Connect(IP, PORT);
                    connected(null);
                    return clientSocket.Connected;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                Dns.BeginGetHostAddresses(ip, StartConnectAsyc, this);
            }
            //这里做一个超时的监测，当连接超过5秒还没成功表示超时  
            //bool success = result.AsyncWaitHandle.WaitOne(5000, false); 
            return true;
        }

        //生成连接的套接字
        private void GenClientSocket(bool isIPV6)
        {
#if UNITY_IOS
            if (isIPV6)
            {
                clientSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
            }
            else
#endif
            {
                clientSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            clientSocket.NoDelay = true;
            LingerOption linger = new LingerOption(false, 0);
            clientSocket.LingerState = linger;
        }

        //开始异步连接，同步的不用调用他
        private void StartConnectAsyc(IAsyncResult asyncConnect)
        {
            if (asyncConnect != null)
            {
                try
                {
                    IPAddress[] address = Dns.EndGetHostAddresses(asyncConnect);
                    if (address.Length > 0)
                    {
                        GenClientSocket(address[0].AddressFamily == AddressFamily.InterNetworkV6);
                    }

                    //启动套接字的循环处理线程
                    Thread thread = new Thread(new ThreadStart(ConnectingThread));
                    thread.IsBackground = true;
                    thread.Start();
                }
                catch (Exception e)
                {
                    Debug.LogError(e.Message);
                    connected(null); //连接失败
                }
            }
            else
            {
                connected(null);
            }
        }

        private void ConnectingThread()
        {
            var res = clientSocket.BeginConnect(IP, PORT, connected, this);
            res.AsyncWaitHandle.WaitOne(ConnectTimeout, true);
            if (!res.IsCompleted)
            {
                clientSocket.Close();
            }
            else
            {
                if (clientSocket != null && clientSocket.Connected)
                {
                    try
                    {
                        status = eSTATUS.STATUS_CONNECTED;
                        //设置为无延迟 
                        clientSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
                        var localip = clientSocket.LocalEndPoint as IPEndPoint;
                        if (localip != null)
                        {
                            LocalIP = localip.Address.ToString();
                            LocalPORT = localip.Port;
                        }
                        //通知事件
                        Group.USocket_OnConnect(this, true);
                        if (asycRead) //异步收取
                        {
                            clientSocket.BeginReceive(buf.GetRaw(), 0, buf.GetRaw().Length, SocketFlags.None, onRecieved, clientSocket);
                        }
                        if (!asycRead || !asycSend)
                        {
                            //启动套接字的循环处理线程
                            Thread thread = new Thread(new ThreadStart(socketRun));
                            thread.IsBackground = true;
                            thread.Start();
                        }
                    }
                    catch (Exception e)
                    {
                        ConnectedFailed();
                        Debug.LogError(e.Message);
                        //OnError.Invoke(this, "连接失败！");
                    }
                }
                else
                {
                    ConnectedFailed();
                }
            }
        }

        /**
         * 关闭连接
         */
        public void Close(bool serverClose = false)
        {
            try
            {
                if (clientSocket != null && clientSocket.Connected)
                {
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                }
            }
            catch (Exception e)
            {
                Debug.LogError(e);
            }
            this.serverClose = serverClose;
            status = eSTATUS.STATUS_CLOSED;
            Group.USocket_OnClose(this, serverClose);
            Debug.Log($"Close:{IP}");
        }

        /**
         * 连接成功
         */
        private void connected(IAsyncResult asyncConnect)
        {
            if (clientSocket != null && clientSocket.Connected)
            {
                try
                {
                    if (asyncConnect != null)
                    {
                        clientSocket.EndConnect(asyncConnect);
                    }
                }
                catch (Exception e)
                {
                    status = eSTATUS.STATUS_CLOSED;
                    Group.USocket_OnConnect(this, false);
                    Debug.LogError(e.Message);
                    //OnError.Invoke(this, "连接失败！");
                }
            }
            else
            {
                ConnectedFailed();
            }
        }

        private void ConnectedFailed()
        {
            status = eSTATUS.STATUS_CLOSED;
            Group.USocket_OnConnect(this, false);
#if UNITY_EDITOR
            Debug.LogError("连接失败");
#endif
            //OnError.Invoke(this, "连接失败！");
        }

        /**
         *发送
         */
        public void Send(Frame frame)
        {
            if (asycSend)
            {
                ISend(frame);
            }
            else
            {
                _SendBuff.Enqueue(frame);
            }
        }


        /**
         * 接收数据
         */
        private void socketRun()
        {
            while (status == eSTATUS.STATUS_CONNECTED)
            {
                // //每隔一段时间发送Ping
                // if (Environment.TickCount>= NextPingTickTime)
                // {
                //     NextPingTickTime = Environment.TickCount + PingRateTick;
                //     SendPing();
                // }

                //接收处理
                //clientSocket != null && clientSocket.Connected 修复：ObjectDisposedException: Cannot access a disposed object.
                if (!asycRead && clientSocket != null && clientSocket.Connected && clientSocket.Poll(TimeOut, SelectMode.SelectRead))
                {
                    try
                    {
                        int len = clientSocket.Receive(buf.GetRaw());
                        if (len > 0)
                        {
                            buf.ReaderIndex(0);
                            buf.WriterIndex(len);
                            while (true)
                            {
                                //解包帧数据
                                Frame frame = Protocal.TranslateFrame(buf);
                                if (frame != null)
                                {
                                    // //如果是Ping请求，进行Ping值计算,而不转发出去
                                    // if (frame.RPCID == (int)eSocketProtocol.Ping)
                                    // {
                                    //     try
                                    //     {
                                    //         int lastSendTicks = frame.GetData().ReadInt();
                                    //         Ping = (Environment.TickCount - lastSendTicks);
                                    //         RPCTest.TPingNum++;
                                    //     }
                                    //     catch (Exception e)
                                    //     {
                                    //     }
                                    // }
                                    // else { 
                                    //转发出去
                                    Group.USocket_OnMessage(this, frame);
                                    //   }
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                        else
                        {
                            Close(true);
                        }
                    }
                    catch (Exception e)
                    {
#if UNITY_EDITOR
                        Debug.LogError(e);
#endif
                        Group.USocket_OnError(this, e.StackTrace + e.Message);
                        Close(true);
                    }
                }
                //发送处理
                //clientSocket != null && clientSocket.Connected 修复：ObjectDisposedException: Cannot access a disposed object.
                if (!asycSend && clientSocket != null && clientSocket.Connected && clientSocket.Poll(TimeOut, SelectMode.SelectWrite))
                {
                    try
                    {
                        Monitor.Enter(_SendBuff);
                        while (_SendBuff.Count > 0 && status == eSTATUS.STATUS_CONNECTED)
                        {
                            var frame = _SendBuff.Dequeue();
                            //封包 
                            frame.End();
                            var buffer = frame.GetData();
                            //保存读取Index和写入Index,便于后面重发
                            buffer.MarkReaderIndex();
                            buffer.MarkWriterIndex();
                            byte[] msg = buffer.GetRaw();
                            clientSocket.Send(msg, buffer.ReaderIndex(), buffer.ReadableBytes(), SocketFlags.None);
                            //如果自动释放内存，释放
                            if (frame.IsSendAutoConsumed)
                            {
                                frame.Consumed();
                            }
                        }
                        Monitor.Exit(_SendBuff);
                    }
                    catch (Exception e)
                    {
                        Group.USocket_OnError(this, e.StackTrace + e.Message);
                        Close(true);
                    }
                }
                //错误处理 
                if (clientSocket != null && clientSocket.Connected && clientSocket.Poll(TimeOut, SelectMode.SelectError))
                {
                    Group.USocket_OnError(this, "TIME OUT");
                    Close(true);
                }

                //避免长时间占用
                if (Group.ThreadSleepTime != 0)
                {
                    Thread.Sleep(Group.ThreadSleepTime);
                }
            }
        }


        #region 异步处理

        /**
         *发送
         */
        public IAsyncResult ISend(Frame frame)
        {
            //封包 
            frame.End();
            var buf = frame.GetData();
            //保存读取Index和写入Index,便于后面重发
            buf.MarkReaderIndex();
            buf.MarkWriterIndex();
            try
            {
                byte[] msg = buf.GetRaw();
                IAsyncResult asyncSend = clientSocket.BeginSend(msg, buf.ReaderIndex(), buf.ReadableBytes(), SocketFlags.None, sended, buf);
                return asyncSend;
            }
            catch (Exception e)
            {
                Group.USocket_OnError(this, e.StackTrace + e.Message);
                if (!clientSocket.Connected)
                {
                    Close();
                }
                return null;
            }
        }

        /**
         * 发送成功的回调
         */
        private void sended(IAsyncResult ar)
        {
            ByteBuf bb = (ByteBuf) ar.AsyncState;
            bb.ReaderIndex(bb.WriterIndex());
            clientSocket.EndSend(ar);
        }

        /**
         * 异步收取信息
         */
        private void onRecieved(IAsyncResult ar)
        {
            try
            {
                System.Net.Sockets.Socket so = (System.Net.Sockets.Socket) ar.AsyncState;
                int len = so.EndReceive(ar);
                if (len > 0)
                {
                    buf.ReaderIndex(0);
                    buf.WriterIndex(len);
                    while (true)
                    {
                        Frame frame = Protocal.TranslateFrame(buf);
                        if (frame != null)
                        {
                            Group.USocket_OnMessage(this, frame);
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (status == eSTATUS.STATUS_CONNECTED)
                    {
                        clientSocket.BeginReceive(buf.GetRaw(), 0, buf.GetRaw().Length, SocketFlags.None, new AsyncCallback(onRecieved), clientSocket);
                    }
                }
                else
                {
                    Group.USocket_OnError(this, "收到的数据内容为空");
                    Close(true);
                }
            }
            catch (Exception e)
            {
                Group.USocket_OnError(this, e.StackTrace + e.Message);
                Close(true);
            }
        }

        #endregion
    }
}